#include "k-means.h"


kmeansCluster::kmeansCluster()
{
  dataNode = NULL ;
  clusterCenter = NULL ;
  dataArray = NULL ;
  minData = NULL ;
  maxData = NULL ;
}

kmeansCluster::~kmeansCluster()
{
  if( dataNode != NULL )
    {
      delete[] dataNode ;
    }

  if( clusterCenter != NULL )
    {
      for( int index = 0 ; index < kCount ; ++ index )
	{
	  if( clusterCenter[index].data != NULL )
	    delete clusterCenter[index].data ;
	  if( clusterCenter[index].preData != NULL ) 
	    delete clusterCenter[index].preData ;
	}
      delete []clusterCenter ;
    }
  if( dataArray != NULL )
    delete []dataArray ;
  if( minData != NULL )
    delete []minData ;
  if( maxData != NULL )
    delete []maxData ;
}
void kmeansCluster::readDataFromPath(const char * _positivePath, const char * _negetivePath)
{
  assert(_positivePath!=NULL&&_negetivePath!=NULL) ;
  
  std::vector<std::string> positiveList ;
  std::vector<std::string> negetiveList ;
  getFileFromPath(_positivePath,positiveList) ;
  getFileFromPath(_negetivePath,negetiveList) ;
  std::string buf ;
  dataHeight = 0 ;
  dataWidth = 0 ;
  long long elems = 0 ;
  for( int index = 0 ; index < positiveList.size() ; ++ index )
    {
      std::string fileName(_positivePath) ;
      fileName += positiveList[index] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      // 初始化数据的宽度
      if( dataHeight == 0 && dataWidth == 0 ) 
	{
	  getline(fileInStream,buf) ;
	  getline(fileInStream,buf) ;
	  for( int i = 0 ; i < buf.size() ; ++ i )
	    if( buf[i] == '\t' ) dataWidth ++ ;
	  dataHeight ++ ;
	  minData = new float[dataWidth] ;
	  maxData = new float[dataWidth] ;
	  std::istringstream ss(buf) ;
	  for( int i = 0 ; i < dataWidth ; ++ i )
	    {
	      float val ;
	      ss >> val ;
	      minData[i] = val ;
	      maxData[i] = val ;
	    }
	}
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  std::istringstream ss(buf) ;
	  for( int i = 0 ; i < dataWidth ; ++ i )
	    {
	      float val ;
	      ss >> val ;
	      if( val > maxData[i] ) maxData[i] = val ;
	      if( val < minData[i] ) minData[i] = val ;
	    }
	  ++ dataHeight ;
	}
    }
  for( int index = 0 ; index < negetiveList.size() ; ++ index )
    {
      std::string fileName(_negetivePath) ;
      fileName += negetiveList[index] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  std::istringstream ss(buf) ;
	  for( int i = 0 ; i < dataWidth ; ++ i )
	    {
	      float val ;
	      ss >> val ;
	      if( val > maxData[i] ) maxData[i] = val ;
	      if( val < minData[i] ) minData[i] = val ;
	    }
	  ++ dataHeight ;
	}
    }
  elems = dataHeight * dataWidth ;
  dataNode = new node[dataHeight] ;
  dataArray = new float[elems] ;
  int indexNode = 0 ;
  long long indexData = 0 ;
  for(  int index = 0 ; index < positiveList.size() ;++ index ) 
    {
      std::string fileName(_positivePath) ;
      fileName += positiveList[index] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) ) 
	{
	  if( buf[0] == '#' ) continue ;
	  dataNode[indexNode].data = &dataArray[indexData] ;
	  std::istringstream ss(buf) ;
	  for( int i = 0 ; i < dataWidth ; ++ i )
	    {
	      float val ;
	      ss >> val ;
	      val = (val - minData[i])*100/maxData[i] ;
	      dataArray[indexData++] = val ;
	    }
	  dataNode[indexNode].indexCluster = -1 ;
	  ++ indexNode ;
	}
    }
  for(  int index = 0 ; index < negetiveList.size() ; ++ index )
    {
      std::string fileName(_negetivePath) ;
      fileName += negetiveList[index] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  dataNode[indexNode].data = &dataArray[indexData] ;
	  std::istringstream ss(buf) ;
	  for( int i = 0 ; i < dataWidth ; ++ i )
	    {
	      float val ;
	      ss >> val ;
	      val = (val-minData[i])*100/maxData[i] ;
	      dataArray[indexData++] = val ;
	    }
	  dataNode[indexNode].indexCluster = -1 ;
	  ++ indexNode ;
	}
    }
}
bool kmeansCluster::testResult()
{
  float sum = 0 ;
  for( int index = 0 ; index < dataHeight ; ++ index )
    {
      float closest = distance(clusterCenter[0].data,dataNode[index].data,dataWidth) ;
      int indexCluster = 0 ;
      for( int indexk = 1 ; indexk < kCount ; ++ indexk )
	{
	  float dis = distance(clusterCenter[indexk].data,dataNode[index].data,dataWidth) ;
	  if( dis < closest ) 
	    {
	      closest = dis ;
	      indexCluster = indexk ;
	    }
	  if( dataNode[index].indexCluster != indexCluster ) return false ;
	}
    }
  std::cout << "~~~~~~~~~~~~~~~~~~~~~~" << std::endl ;
  return true ;
}
void kmeansCluster::trainKmeans(const char* _positivePath, const char* _negetivePath,int _kCount, int _clusterIterator, const char * _kmeansModelFile)
{
  assert(_positivePath!=NULL&&_negetivePath!=NULL&&_kmeansModelFile!=NULL) ;
  kCount = _kCount ;
  clusterIterator = _clusterIterator ;
  readDataFromPath(_positivePath,_negetivePath) ;

  if( clusterCenter != NULL )
    delete []clusterCenter ;
  clusterCenter = new ClusterCenter[kCount] ;
  for( int index = 0 ; index <kCount ; ++ index )
  {
      clusterCenter[index].data = new float[dataWidth] ;
      clusterCenter[index].preData = new float[dataWidth] ;
  }
  
#ifdef __K_MEANS_DEBUG__
  INFO2("dataWidth",dataWidth) ;
  INFO2("dataHeight",dataHeight) ;
  // 初始化 kmeans的k个中心点。
  for( int index = 0 ; index < kCount ; ++ index )
    {
      int indexRand = static_cast<float>(index) / kCount * dataHeight ;
      for( int index_data = 0 ; index_data < dataWidth ; ++ index_data )
	clusterCenter[index].data[index_data] = dataNode[indexRand].data[index_data] ;
      clusterCenter[index].clusterCount = 0 ;
    }
  float thresholdCluster = 0; 
  int iterTime = 0 ;
  while( iterTime < clusterIterator )
    {
      // 将当期中心值保存
      for( int index = 0 ; index < kCount ; ++ index )
	for( int indexData = 0 ; indexData < dataWidth ; ++ indexData )
	  clusterCenter[index].preData[indexData] = clusterCenter[index].data[indexData] ;
      // 聚类
      for( int index = 0 ; index < dataHeight ; ++ index )
	{
	  float min = 10000000000 ;
	  int mink = 0 ;
	  for( int indexk = 0 ; indexk < kCount; ++ indexk )
	    {
	      float dis = distance(dataNode[index].data,clusterCenter[indexk].data,dataWidth) ;
	      if( dis < min ) 
		{
		  min = dis ;
		  mink = indexk ;
		}
	    }
 	  ++ clusterCenter[mink].clusterCount ;
	  dataNode[index].indexCluster = mink ;
	}
      /*
      std::ofstream retOutFile("temp") ;
      int sum = 0 ;
      for( int index = 0 ; index < kCount ; ++ index )
	{
	  retOutFile << index << " " << clusterCenter[index].clusterCount << std::endl ;
	  sum += clusterCenter[index].clusterCount ;
	}
      retOutFile <<"sum:" << sum << std::endl ;
      retOutFile.close() ;
      */
      updateCenter() ;
      ++ iterTime ;
      INFO2("iterTime",iterTime) ;
      if( testResult() ) break ;
    } 
  delete [] dataArray ;
  delete [] dataNode ;
  dataArray = NULL ;
  dataNode = NULL ;
  INFO1("train kmeans model:") ;
  INFO2("iter time:", iterTime) ;
  INFO2("k counts:" , kCount) ;
#else
  kmeansByOpencv();
#endif
  saveKmeansModel(_kmeansModelFile) ;
}
void kmeansCluster::updateCenter()
{
  float ** center = new float*[kCount] ;
  for( int index = 0 ; index < kCount ; ++ index )
    {
      center[index] = new float[dataWidth] ;
      for( int indexData = 0 ; indexData < dataWidth ; ++ indexData )
	center[index][indexData] = 0 ;
    }
  for( int index = 0 ; index < dataHeight ; ++ index )
    {
      int k = dataNode[index].indexCluster ;
      for( int indexData = 0 ; indexData < dataWidth ; ++ indexData )
	center[k][indexData] += dataNode[index].data[indexData] ;
    }
  for( int index = 0 ; index < kCount ; ++ index )
    {
      for( int indexData = 0 ; indexData < dataWidth ; ++ indexData )
	clusterCenter[index].data[indexData] = center[index][indexData]/clusterCenter[index].clusterCount ;
      clusterCenter[index].clusterCount = 0 ;
    }
  for( int index = 0 ; index < kCount ; ++ index )
    delete [] center[index] ;
  delete [] center ;
}
float kmeansCluster::distance(float* _p1, float* _p2, int n)
{
  float ret = 0 ;
  for( int index = 0 ; index < n ; ++ index )
    ret += (_p1[index] - _p2[index])*(_p1[index] - _p2[index]) ;
  return sqrt(ret) ;
}
void kmeansCluster::getHistogramFromPath(const char* _inPath, const char* _outPath)
{
  assert(_inPath!=NULL&&_outPath!=NULL);
  std::vector<std::string> inList ;
  getFileFromPath(_inPath,inList) ;
  for( int index = 0 ; index < inList.size() ; ++ index )
    {
      std::string preffix = inList[index] ;
      std::string::size_type pos = preffix.find('.') ;
      preffix.erase(pos) ;
      std::string fileInName(_inPath) ;
      fileInName += inList[index] ;
      std::string fileOutName(_outPath) ;
      fileOutName += preffix + ".histogram" ;
      //if( access(fileOutName.c_str(),F_OK) != 0 )
	getHistogramBykmeans(fileInName.c_str(),fileOutName.c_str()) ;
    }
}
void kmeansCluster::getHistogramBykmeans(const char* _inFile, const char* _outFile)
{
  assert(_inFile!=NULL&&_outFile!=NULL) ;
  std::ifstream fileInStream(_inFile) ;
  std::ofstream fileOutStream(_outFile) ;
  if( !fileInStream.is_open() || !fileOutStream.is_open() )
    {
      ERROR("can not open the stream") ;
      exit(-1) ;
    }
  int *histogram = new int[kCount] ;
  memset(histogram,0,sizeof(int)*kCount) ;
  std::string buf ;
  float *words = new float[dataWidth] ;
  int sum = 0 ;
  while(getline(fileInStream,buf)) 
    {
      if( buf[0] == '#' ) continue ;
      float min = 10000 ;
      int mink = 0 ;
      std::istringstream ss(buf) ;
      for( int index = 0 ; index < dataWidth ; ++ index )
	ss >> words[index] ;
      for( int index = 0 ; index < dataWidth ; ++ index )
	words[index] = (words[index]-minData[index])*100/maxData[index] ;
      for( int index = 0 ; index < kCount ; ++ index )
	{
	  float dis = distance(clusterCenter[index].data,words,dataWidth) ;
	  if( dis < min ) 
	    {
	      min = dis ;
	      mink = index ;
	    }
	}
      ++ histogram[mink] ;
      ++ sum ;
    }
  for( int index = 0 ; index < kCount ; ++ index )
    fileOutStream << static_cast<float>(histogram[index])/sum*1000 << "\t" ;
  fileOutStream << std::endl ;
  delete []words ;
  delete []histogram ;
}
void kmeansCluster::saveKmeansModel(const char* _kmeansFile)
{
  assert( _kmeansFile!=NULL ) ;
  std::ofstream fileOutStream(_kmeansFile) ;
  if( !fileOutStream.is_open() )
    {
      INFO2("can not open the file",_kmeansFile) ;
      exit(-1) ;
     }
  fileOutStream << "dataWidth: " << dataWidth << std::endl ;
  fileOutStream << "kCount: " << kCount << std::endl ;
  for( int index = 0 ; index < kCount ; ++ index )
    {
      for( int indexData = 0; indexData < dataWidth ; ++ indexData )
	fileOutStream << clusterCenter[index].data[indexData] << "\t" ; 
      fileOutStream << std::endl ;
    }
  for( int index = 0 ; index < dataWidth ; ++ index )
    fileOutStream << minData[index] << "\t" ;
  fileOutStream << std::endl ;
  for( int index = 0 ; index < dataWidth ; ++ index )
    fileOutStream << maxData[index] << "\t" ;
  fileOutStream << std::endl ;
  INFO1("kmeans Model save finished!") ;
}
void kmeansCluster::loadKmeansModel(const char* _kmeansModelFile)
{
  assert(_kmeansModelFile!=NULL) ;
  if( access(_kmeansModelFile,F_OK) ) 
    {
      INFO2("file is not exist!",_kmeansModelFile) ;
      exit(-1) ;
    }
  std::ifstream fileInStream(_kmeansModelFile) ;
  if( !fileInStream.is_open() ) 
    {
      INFO2("can not open the file",_kmeansModelFile) ;
      exit(-1) ;
    }
  std::string label ;
  fileInStream >> label >> dataWidth ;
  if( label != "dataWidth:" ) 
    {
      ERROR("kmeans model format error") ;
      INFO2("dataWidth:",label) ;
      exit(-1) ;
    }
  fileInStream >> label >> kCount ;
  if( label != "kCount:" )
    {
      ERROR("kmeans model format error") ;
      INFO2("kCount:",label) ;
      exit(-1) ;
    }
  clusterCenter = new ClusterCenter[kCount] ;
  for( int index = 0 ; index < kCount ; ++ index )
    {
      clusterCenter[index].data = new float[dataWidth] ;
      clusterCenter[index].preData = NULL ;
      for( int indexData = 0 ; indexData < dataWidth ; ++ indexData )
	fileInStream >> clusterCenter[index].data[indexData] ;
    }
  minData = new float[dataWidth] ;
  maxData = new float[dataWidth] ;
  for( int index = 0 ; index < dataWidth ; ++ index )
    fileInStream >> minData[index]  ;
  for( int index = 0 ; index < dataWidth ; ++ index )
    fileInStream >> maxData[index] ;
  INFO1("kmeans Model load finished!") ;
}
void kmeansCluster::getFileFromPath(const char * _path , std::vector<std::string>& _fileVector)
{
  DIR * pDir ;
  struct dirent * pDirent ;
  _fileVector.clear() ;
  if( _path == NULL )
    {
      ERROR("file path is NULL!") ;
      exit(-1) ;
    }
  pDir = opendir(_path) ;
  while( (pDirent = readdir(pDir)) != NULL )
    {
      INFO2("file in path",pDirent->d_name) ; 
      if( !(pDirent->d_type & DT_DIR) )
	{
	  _fileVector.push_back(std::string(pDirent->d_name)) ;
	}
    }
  closedir(pDir) ;
}

void kmeansCluster::kmeansByOpencv()
{
    cv::Mat data ;
    data.create(cvSize(dataWidth,dataHeight),CV_32FC1) ;
    cv::Mat center ;
    center.create(cvSize(dataWidth,kCount),CV_32FC1) ;
    cv::Mat clustersIndex ;
    for( int indexRow = 0 ; indexRow < dataHeight ; ++ indexRow )
    {
        float * ptr = (float*)data.ptr(indexRow) ;
        for( int indexCol = 0 ; indexCol < dataWidth ; ++ indexCol )
            ptr[indexCol] = dataNode[indexRow].data[indexCol] ;
    }
    INFO1("train kmeans model!") ;
    double compactness = cv::kmeans(data,kCount,clustersIndex,cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,100,30.0),2,0,center) ;
    INFO2("compactness",compactness) ;
    INFO1("kmeans model train finished!") ;
    for( int indexRow = 0 ; indexRow < kCount ; ++ indexRow )
    {
        float * ptr = (float*)center.ptr(indexRow) ;
        for( int indexCol = 0 ; indexCol < dataWidth ; ++ indexCol )
            clusterCenter[indexRow].data[indexCol] = ptr[indexCol] ;
    }
}
